//
// Created by y on 2017-12-17.
//

#include "Video.h"

VideoPlayer::VideoPlayer(JavaCallFunc *javaCall, AudioPlayer *audio, WlPlayStatus *playStatus) {
    streamIndex = -1;
    clock = 0;
    wljavaCall = javaCall;
    Audio = audio;
    queue = new Queue(playStatus);
    PlayStatus = playStatus;
}

void VideoPlayer::release() {
    if(LOG_SHOW)
    {
        LOGE("开始释放audio ...");
    }

    if(PlayStatus != NULL)
    {
        PlayStatus->exit = true;
    }
    if(queue != NULL)
    {
        queue->noticeThread();
    }
    int count = 0;
    while(!isExit || !isExit2)
    {
        if(LOG_SHOW)
        {
            LOGE("等待渲染线程结束...%d", count);
        }

        if(count > 1000)
        {
            isExit = true;
            isExit2 = true;
        }
        count++;
        av_usleep(1000 * 10);
    }
    if(queue != NULL)
    {
        queue->release();
        delete(queue);
        queue = NULL;
    }
    if(wljavaCall != NULL)
    {
        wljavaCall = NULL;
    }
    if(Audio != NULL)
    {
        Audio = NULL;
    }
    if(avCodecContext != NULL)
    {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
    }
    if(PlayStatus != NULL)
    {
        PlayStatus = NULL;
    }
}

void *decodVideoT(void *data)
{
    VideoPlayer *Video = (VideoPlayer *) data;
    Video->decodVideo();
    pthread_exit(&Video->videoThread);

}

void *codecFrame(void *data)
{
    VideoPlayer *Video = (VideoPlayer *) data;

    while(!Video->PlayStatus->exit)
    {
        if(Video->PlayStatus->seek)
        {
            continue;
        }
        Video->isExit2 = false;
        if(Video->queue->getAvFrameSize() > 20)
        {
            continue;
        }
        if(Video->codecType == 1)
        {
            if(Video->queue->getAvPacketSize() == 0)//加载
            {
                if(!Video->PlayStatus->load)
                {
                    Video->wljavaCall->onLoad(THREAD_CHILD, true);
                    Video->PlayStatus->load = true;
                }
                continue;
            } else{
                if(Video->PlayStatus->load)
                {
                    Video->wljavaCall->onLoad(THREAD_CHILD, false);
                    Video->PlayStatus->load = false;
                }
            }
        }
        AVPacket *packet = av_packet_alloc();
        if(Video->queue->getAvpacket(packet) != 0)
        {
            av_packet_free(&packet);
            av_free(packet);
            packet = NULL;
            continue;
        }

        int ret = avcodec_send_packet(Video->avCodecContext, packet);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
            av_packet_free(&packet);
            av_free(packet);
            packet = NULL;
            continue;
        }
        AVFrame *frame = av_frame_alloc();
        ret = avcodec_receive_frame(Video->avCodecContext, frame);
        if (ret < 0 && ret != AVERROR_EOF) {
            av_frame_free(&frame);
            av_free(frame);
            frame = NULL;
            av_packet_free(&packet);
            av_free(packet);
            packet = NULL;
            continue;
        }
        Video->queue->putAvframe(frame);
        av_packet_free(&packet);
        av_free(packet);
        packet = NULL;
    }
    Video->isExit2 = true;
    pthread_exit(&Video->decFrame);
}


void VideoPlayer::playVideo(int type) {
    codecType = type;
    if(codecType == 0)
    {
        pthread_create(&decFrame, NULL, codecFrame, this);
    }
    pthread_create(&videoThread, NULL, decodVideoT, this);

}

void VideoPlayer::decodVideo() {
    while(!PlayStatus->exit)
    {
        isExit = false;
        if(PlayStatus->pause)//暂停
        {
            continue;
        }
        if(PlayStatus->seek)
        {
            if(wljavaCall != NULL){
                wljavaCall->onLoad(THREAD_CHILD, true);
            }
            else{
                LOGE("call onLoad error...");
            }
            PlayStatus->load = true;
            continue;
        }
        if(queue->getAvPacketSize() == 0)//加载
        {
            if(!PlayStatus->load)
            {
                if(wljavaCall != NULL){
                    wljavaCall->onLoad(THREAD_CHILD, true);
                }
                else{
                    LOGE("call onLoad error...");
                }
                PlayStatus->load = true;
            }
            continue;
        } else{
            if(PlayStatus->load)
            {
                if(wljavaCall != NULL){
                    wljavaCall->onLoad(THREAD_CHILD, false);
                }
                else{
                    LOGE("call onLoad error...");
                }
                PlayStatus->load = false;
            }
        }
        if(codecType == 1)
        {
            AVPacket *packet = av_packet_alloc();
            if(queue->getAvpacket(packet) != 0)
            {
                av_free(packet->data);
                av_free(packet->buf);
                av_free(packet->side_data);
                packet = NULL;
                continue;
            }
            double time = packet->pts * av_q2d(time_base);

            if(LOG_SHOW)
            {
                LOGE("video clock is %f", time);
                LOGE("audio clock is %f", Audio->clock);
            }
            if(time < 0)
            {
                time = packet->dts * av_q2d(time_base);
            }

            if(time < clock)
            {
                time = clock;
            }
            clock = time;
            double diff = 0;
            if(Audio != NULL)
            {
                diff = Audio->clock - clock;
            }
            playcount++;
            if(playcount > 500)
            {
                playcount = 0;
            }
            if(diff >= 0.5)
            {
                if(frameratebig)
                {
                    if(playcount % 3 == 0 && packet->flags != AV_PKT_FLAG_KEY)
                    {
                        av_free(packet->data);
                        av_free(packet->buf);
                        av_free(packet->side_data);
                        packet = NULL;
                        continue;
                    }
                } else{
                    av_free(packet->data);
                    av_free(packet->buf);
                    av_free(packet->side_data);
                    packet = NULL;
                    continue;
                }
            }

            delayTime = getDelayTime(diff);
            if(LOG_SHOW)
            {
                LOGE("delay time %f diff is %f", delayTime, diff);
            }

            av_usleep(delayTime * 1000);
            if(wljavaCall != NULL){
                wljavaCall->onVideoInfo(THREAD_CHILD, clock, duration);
                wljavaCall->onDecMediacodec(THREAD_CHILD, packet->size, packet->data, 0);
            }
            av_free(packet->data);
            av_free(packet->buf);
            av_free(packet->side_data);
            packet = NULL;
        }
        else if(codecType == 0)
        {
            AVFrame *frame = av_frame_alloc();
            if(queue->getAvframe(frame) != 0)
            {
                av_frame_free(&frame);
                av_free(frame);
                frame = NULL;
                continue;
            }
            if ((framePts = av_frame_get_best_effort_timestamp(frame)) == AV_NOPTS_VALUE)
            {
                framePts = 0;
            }
            framePts *= av_q2d(time_base);
            clock = synchronize(frame, framePts);
            double diff = 0;
            if(Audio != NULL)
            {
                diff = Audio->clock - clock;
            }
            delayTime = getDelayTime(diff);
            if(LOG_SHOW)
            {
                LOGE("delay time %f diff is %f", delayTime, diff);
            }
//            if(diff >= 0.8)
//            {
//                av_frame_free(&frame);
//                av_free(frame);
//                frame = NULL;
//                continue;
//            }

            playcount++;
            if(playcount > 500)
            {
                playcount = 0;
            }
            if(diff >= 0.5)
            {
                if(frameratebig)
                {
                    if(playcount % 3 == 0)
                    {
                        av_frame_free(&frame);
                        av_free(frame);
                        frame = NULL;
                        queue->clearToKeyFrame();
                        continue;
                    }
                } else{
                    av_frame_free(&frame);
                    av_free(frame);
                    frame = NULL;
                    queue->clearToKeyFrame();
                    continue;
                }
            }

            av_usleep(delayTime * 1000);
            if(wljavaCall != NULL) {
                wljavaCall->onVideoInfo(THREAD_CHILD, clock, duration);
                wljavaCall->onGlRenderYuv(THREAD_CHILD, frame->linesize[0], frame->height, frame->data[0], frame->data[1], frame->data[2]);
            }
            av_frame_free(&frame);
            av_free(frame);
            frame = NULL;
        }
    }
    isExit = true;

}

VideoPlayer::~VideoPlayer() {
    if(LOG_SHOW)
    {
        LOGE("video s释放完");
    }
}

double VideoPlayer::synchronize(AVFrame *srcFrame, double pts) {
    double frame_delay;

    if (pts != 0)
        video_clock = pts; // Get pts,then set video clock to it
    else
        pts = video_clock; // Don't get pts,set it to video clock

    frame_delay = av_q2d(time_base);
    frame_delay += srcFrame->repeat_pict * (frame_delay * 0.5);

    video_clock += frame_delay;

    return pts;
}

double VideoPlayer::getDelayTime(double diff) {

    if(LOG_SHOW)
    {
        LOGD("audio video diff is %f", diff);
    }

    if(diff > 0.003)
    {
        delayTime = delayTime / 3 * 2;
        if(delayTime < rate / 2)
        {
            delayTime = rate / 3 * 2;
        }
        else if(delayTime > rate * 2)
        {
            delayTime = rate * 2;
        }

    }
    else if(diff < -0.003)
    {
        delayTime = delayTime * 3 / 2;
        if(delayTime < rate / 2)
        {
            delayTime = rate / 3 * 2;
        }
        else if(delayTime > rate * 2)
        {
            delayTime = rate * 2;
        }
    }else if(diff == 0)
    {
        delayTime = rate;
    }
    if(diff > 1.0)
    {
        delayTime = 0;
    }
    if(diff < -1.0)
    {
        delayTime = rate * 2;
    }
    if(fabs(diff) > 10)
    {
        delayTime = rate;
    }
    return delayTime;
}

void VideoPlayer::setClock(int secds) {
    clock = secds;
}



